#pragma once

#include <iostream>
#include <string>
#include <poll.h>
#include "Socket.hpp"
#include "Log.hpp"
#include <memory>

using namespace std;

const static uint16_t defalutport = 8888;
const static int gbacklog = 8;
const static int num = 1024;

class PollServer
{
private:
    void HandlerEvent()
    {
        for (int i = 0; i < _num; i++)
        {
            // 是否监控
            if (_rfds[i].fd == -1)
                continue;

            // 是否就绪
            int fd = _rfds[i].fd;

            if (_rfds[i].revents & POLLIN)
            {
                // 是新连接到来，还是新数据到来
                // 新连接到来
                if (fd == _listensock->GetSockFd())
                {
                    lg.LogMessage(Info, "get a new link\n");

                    string clientip;
                    uint16_t cilentport;
                    // 由于select已经检测到listensock已经就绪了，这里不会阻塞
                    int sockfd = _listensock->AcceptConnection(&clientip, &cilentport);
                    if (sockfd == -1)
                    {
                        lg.LogMessage(Error, "accept error\n");
                        continue;
                    }
                    lg.LogMessage(Info, "get a client , client info# %s %d , fd:%d\n", clientip.c_str(), cilentport, sockfd);

                    // 这里已经获取连接成功，由于底层数据不一定就绪
                    // 所以这里需要将新连接的文件描述符交给poll托管
                    // 只需将文件描述符加入到_rfds即可
                    int pos = 0;
                    for (; pos < _num; pos++)
                    {
                        if (_rfds[pos].fd == -1)
                        {
                            _rfds[pos].fd = sockfd;
                            _rfds[pos].events |= POLLIN;
                            break;
                        }
                    }

                    // 当存储上限时，可以选择扩容，由于poll并不是很重要，这里我为了方便就直接关闭文件描述符
                    if(pos == _num)
                    {
                        close(sockfd);
                        lg.LogMessage(Warning, "server is full...!\n");
                    }
                }
                else
                {  // 是新数据来了
                    // 这里读是有问题的
                    char buffer[1024];
                    bool flag = recv(fd,buffer,1024,0);
                    if(flag)  // 读取成功
                    {
                        lg.LogMessage(Info,"client say# %s\n",buffer);
                    }
                    else  // 读取失败
                    {
                        lg.LogMessage(Warning,"cilent quit !! close fd : %d\n",fd);
                        close(fd);
                        _rfds[i].fd = -1;
                        _rfds[i].events = 0;
                        _rfds[i].revents = 0;
                    }
                }
            }
        }
    }

public:
    PollServer(uint16_t port = defalutport)
        : _port(port), _listensock(new TcpSocket()), _isrunning(false), _num(num),_rfds(new pollfd[_num])
    {
    }

    void Init()
    {
        _listensock->BuildListenSocketMethod(_port, gbacklog);
        for (int i = 0; i < _num; i++)
        {
            _rfds[i].fd = -1;
            _rfds[i].events = 0;
            _rfds[i].revents = 0;
        }
        _rfds[0].fd = _listensock.get()->GetSockFd();
        _rfds[0].events |= POLLIN;
    }

    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            PrintDebug();

            int timeout = 1000;
            ssize_t n = poll(_rfds,_num,timeout);

            switch (n)
            {
            case -1:
            {
                lg.LogMessage(Fatal, "select Error\n");
                break;
            }
            case 0:
            {
                lg.LogMessage(Info, "select timeout...");
                break;
            }
            default:
            {
                lg.LogMessage(Info, "select success , begin handler event\n");
                HandlerEvent();
                break;
            }
            }
        }
        _isrunning = false;
    }

    void Stop()
    {
        _isrunning = false;
    }

    // 查看当前哪些文件描述符需要被监控
    void PrintDebug()
    {
        std::cout << "current select rfds list is : ";
        for (int i = 0; i < _num; i++)
        {
            if (_rfds[i].fd == -1)
                continue;
            else
                std::cout << _rfds[i].fd << " ";
        }
        std::cout << std::endl;
    }

    ~PollServer() 
    {
        delete[] _rfds;
    }

private:
    unique_ptr<Socket> _listensock;
    uint16_t _port;
    bool _isrunning;

    int _num;
    struct pollfd* _rfds;
};